
/**
 * Kohive.Window
 * @extends Ext.Window
 * Provides customised Window behaviour for Kohive windows
 */
Kohive.Window = Ext.extend(Ext.Window, {

	originalStore					: null,

	constructor						: function(config) {
		config = config || {};
		Ext.apply(config, {
					minWidth	: 600,
					minHeight	: 400
				});

		if (this.onStoreLoad) {
			this.onStoreLoad = this.onStoreLoad.createSequence(function(store) {
						this.updateFiltersTask = new Ext.util.DelayedTask(
								this.updateFilters, this
						);
						this.updateFiltersTask.delay(0);
					}, this);
		}

		Kohive.Window.superclass.constructor.call(this, config);
	},

	initComponent					: function() {
		Ext.applyIf(this, {
					animateTarget	: Kohive.OS.getApplicationLauncher().el,
					closeAction		: 'hide',
					constrain		: true,
					width			: 635,
					height			: 400,
					stateful		: false, // TODO, put this back when thigns are fixed when loading apps twice
					shadow			: 'frame',
					shadowOffset	: 20,
					autoScroll		: true,
					header			: true,
					layout			: 'fit',
					hasDefaultTools	: true,
					hbar			: true,
					bbar			: true
				});

		// this.maximizable = (this.initialConfig.maximizable === false) ? false : true;
		this.maximizable = false;

		// Add the dataview to the config only if it isn't been set
		if (!this.items)
			this.items = [this.getDataView()];

		// Check if hbar is defined and if it is true/array
		if (this.hbar && typeof this.hbar == "boolean")
			this.hbar = this.getDefaultHBar();

		// Check if bbar is defined and if it is true/array
		if (this.bbar && typeof this.bbar == "boolean")
			this.bbar = this.getDefaultStatusBar();

		// Check if panel is defined and if it is add the default panel
		if (this.filterPanelConfig && typeof this.filterPanelConfig != "object")
			this.filterPanelConfig = this.getDefaultPanel();

		// open the window in a random position if x and y not set
		if (this.x === undefined || this.y === undefined) {
			var vs = Ext.getBody().getViewSize();
			var viewWidth = vs.width;
			var viewHeight = vs.height;

			var x = Math.max((Math.random() * viewWidth) - this.width, 0) + 92;
			var y = Math.max((Math.random() * viewHeight - 100) - this.height,
					0
			)
					+ 60;

			this.x = this.x === undefined ? x : this.x;
			this.y = this.y === undefined ? y : this.y;
		};

		// setup the header tools
		this.initHeaderTools();

		// ensure HeaderBar plugin installed if required
		if (this.hbar && !this.plugins) {
			this.plugins = [new Ext.ux.HeaderToolbar()];
		};

		// ensure WindowSidePanel plugin installed if required
		if (this.filterPanelConfig && !this.plugins) {
			this.plugins = [new Ext.ux.WindowSidePanel()];
		} else if (this.filterPanelConfig && this.plugins) {
			this.plugins.push(new Ext.ux.WindowSidePanel());
		};

		// check if the new form exists and if it does get it's event
		if (this.newForm) {
			this.relayEvents(this.newForm, ['create']);
		};

		this.on('render', this.initBarMouseOver, this);
		this.on('render', this.checkIfMaximizable, this);
		this.on('render', this.setupEditable, this);
		this.on('render', this.addStatusBarEvents, this);
		this.on('activate', this.addHistoryToken, this);
		this.on('hide', this.removeHistoryToken, this);

		Kohive.Window.superclass.initComponent.apply(this, arguments);

		this.initFilterListeners();
	},

	initFilterListeners				: function() {
		if (this.store) {
			this.store.on('update',
					function(store, updatedRecord, method, name) {
						if (name != "toggled") {
							this.updateFiltersTask.delay(2000,
									this.updateFilters, this, [updatedRecord,
											method, name]
							);
						}
					}, this
			);
		}
	},

	updateFilters					: function(updatedRecord, method, name) {
		Ext.each(this.windowSidePanel.filters, function(filter) {
					filter.clear();
				}, this);

		// Note that originalStore is not an Ext.data.Store :-)
		Ext.each(this.originalStore, function(record) {
					Ext.each(this.windowSidePanel.filters, function(filter) {
								filter.refresh(record, updatedRecord, method,
										name
								);
							}, this);
				}, this);

		// Ext.each(this.windowSidePanel.filters, function (filter) {
		// filter.sort();
		// }, this);

		this.windowSidePanel.fireEvent('refreshed');
	},

	initBarMouseOver				: function() {
		this.on('statusbar-event-start', function(msg) {
					this.showMessage(msg);
				}, this);

		this.on('statusbar-event-stop', this.hideLoading, this);
	},

	checkIfMaximizable				: function() {
		if (!this.maximizable) {
			this.el.addClass('x-window-not-maximizable');
		};
	},

	/**
	 * 
	 */
	initHeaderTools					: function() {
		// create the tools array if none is defined
		if (!this.tools)
			this.tools = [];

		// add default tools (which is a bug icon)
		if (this.hasDefaultTools) {
			this.tools.push({
						id		: 'bug',
						handler	: function() {
							feedback_widget.show();
						}
					});
		};

		// add the collapser thingy majigger if the header toolbar is defined
		if (this.hbar) {
			this.tools.push({
						id		: 'collapse-header',
						scope	: this,
						handler	: function() {
							if (this.toggleHeaderToolbar)
								this.toggleHeaderToolbar();
						}
					});
		};

		// check if there is a help message defined
		if (this.initialConfig.helpMessage) {
			this.tools.push({
						id		: 'help',
						cls		: 'x-tools-help-btn',
						scope	: this,
						handler	: this.showHelpHUD
					});
		};
	},

	/**
	 * 
	 */
	initHelpHud						: function() {
		this.helpHud = new Ext.ux.HUD({
					cls		: 'x-hud-help',
					html	: this.initialConfig.helpMessage,
					header	: false,
					width	: 'auto',
					poker	: false
				});

		this.helpHud.render();
		this.helpHud.hide();
	},

	/**
	 * 
	 */
	showHelpHUD						: function() {
		// create the hud if not already done
		if (!this.helpHud)
			this.initHelpHud();

		// find the help button and get the element of it
		var el = this.el.child('.x-tool-help');

		// update the anchorToEl property and position/show it
		this.helpHud.anchorToEl = el;
		this.helpHud.showHUD([0, 0], 'tr-br', 1, null, false);
	},

	/**
	 * A Ext.CompositeElement of all the pods in the application
	 */
	pods							: null,

	/**
	 * The current toggle all state, defaults to false
	 */
	toggleAllState					: true,

	/**
	 * True when something in the pod is currently being edited.
	 */
	editing							: false,

	/**
	 * The current editable field
	 */
	editableField					: null,

	onEsc							: Ext.emptyFn,

	/**
	 * Function description
	 * @param {Object} config Configuration properties for the dataview
	 * @return {Ext.data.DataView} The dataview
	 */
	getDataView						: function(config) {
		var config = config || {};

		this.sortableDataView = new Ext.ux.SortableDataViewPanel({
			dataViewConfig		: Ext.applyIf(config, this.getDataViewConfig()),
			columnPanelConfig	: this.columnPanelConfig || {},
			mainPanelBefore		: [this.newForm]
		}
		);

		this.sortableDataView.columns
				.on('changesortorder', this.initPods, this);

		return this.sortableDataView;
	},

	/**
	 * Returns the default dataview config
	 * @return {Object} default dataview config
	 */
	getDataViewConfig				: function() {
		this.store.on('update', function(store) {
					this.fireEvent('update-filter-bar', store);
				}, this);

		this.store.on('beforeload', function(store) {
					this.setToggledStates(store);
				}, this);

		this.store.on('load', function(store) {
					this.toggleNewFormIfStoreIsEmpty();
					this.fireEvent('update-filter-bar', store);
					this.viewOnLoad(store);
					this.getToggledStates(store);
					if (this.initialConfig.hasTags === true)
						this.initTagsStore(store);

					// update the original store if the URL hasn't got a ? in it (filtered)
					if (store.proxy.conn.url.split('?').length == 1) {
						this.originalStore = store.data.items;
					};
				}, this);

		return {
			emptyText		: '<div class="no-results">' + this.getEmptyText()
					+ '</div>',
			itemSelector	: '.x-pod',
			overClass		: 'x-over',
			tpl				: this.model.dataView,
			store			: this.store,
			listeners		: {
				scope			: this,
				'mouseenter'	: this.viewOnMouseOver,
				'mouseleave'	: this.viewOnMouseOut,
				'click'			: this.viewOnClick
			}
		};
	},

	/**
	* Called before a record is updated
	*/
	onBeforeUpdate					: function() {
		this.showSubmitting();
	},

	/**
	* Creates a new Ext.ux.Editable instance bound to the dataview
	*/
	setupEditable					: function() {
		if (!this.sortableDataView)
			return;

		this.editable = new Ext.ux.Editable(this.sortableDataView.dataview, {
					view			: this, // Hack so functions like reply() (message board) can access the window view
					model			: this.model, // Hack so function like reply() will have the right scope
					clickActions	: this.model.prototype.clickActions
				});

		this.editable.on('update', this.onBeforeUpdate, this);
		this.relayEvents(this.editable, ['update']);

		this.sortableDataView.dataview.store.on('load',
				this.initalizeEditableMouseover, this
		);

		this.showSuccess();
	},

	/**
	* Called whenever the user changes the filters applied in the filter bar. Fires the 'filter' event
	* @param {Object} filtersApplied The object of currently applied filters.  Something like:
	* {authors: [1, 2, 5], tags: ['cat', 'dog']}
	*/
	onFiltersUpdated				: function(filters) {
		// this.fireEvent('filter', filtersApplied);

		// TODO do this the proper way, until then, do without events..
		if (!this.store)
			return;

		// for some reason passing the params option to findAll isn't working, so build url manually :/
		var url = Kohive.OS.scopedUrl(this.model.urlName
				|| this.model.modelName.toLowerCase().pluralize()), conditions = [];

		// tags = filters.tags,
		// authors = filters.authors,
		// recipients = filters.assignee_membership_id,
		// priority = filters.priority,
		// effort = filters.effort,
		// type = filters.type,
		// flagged = filters.flagged,
		// state = filters.state,
		// milestones = filters.milestones,
		// conditions = [];
		// 
		// if (tags && tags.length > 0) conditions.push(String.format("tags={0}", tags.join(',')));
		// if (authors && authors.length > 0) conditions.push(String.format("membership_ids={0}", authors.join(',')));
		// if (recipients && recipients.length > 0) conditions.push(String.format("recipient_membership_ids={0}", recipients.join(',')));
		// if (priority && priority.length > 0) conditions.push(String.format("priority={0}", priority.join(',')));
		// if (effort && effort.length > 0) conditions.push(String.format("effort={0}", effort.join(',')));
		// if (effort && effort.length > 0) conditions.push(String.format("effort={0}", effort.join(',')));
		// if (type && type.length > 0) conditions.push(String.format("type={0}", type.join(',')));
		// if (flagged && flagged.length > 0) conditions.push(String.format("flagged={0}", flagged.join(',')));
		// if (state && state.length > 0) conditions.push(String.format("state={0}", state.join(',')));
		// if (milestones && milestones.length > 0) conditions.push(String.format("milestones={0}", milestones.join(',')));

		// loop through each of the filters and add them to the conditions
		for (filter in filters) {
			if (filters[filter].length > 0)
				conditions.push(String.format(filter + "={0}", filters[filter]
								.join(',')));
		};

		// update the stores url + reload the store
		if (conditions.length > 0)
			this.store.proxy.conn.url = url + '?' + conditions.join("&");
		else
			this.store.proxy.conn.url = url;

		this.store.load();
	},

	/**
	 * Toggles the new form panel if the store is empty, when the store loads
	 */
	toggleNewFormIfStoreIsEmpty		: function() {
		if (this.initalized)
			return;

		// Check how many items is in the store, and then check if it equals to
		// 0. if it does, toggle the new form panel
		if (this.store.totalLength == 0)
			this.toggleNewForm.defer(50, this);

		this.initalized = true;
	},

	tagsStore						: null,

	/**
	 * 
	 */
	initTagsStore					: function(store) {
		var array = [], storeArray = [];

		// loop through each of the records/tags
		store.each(function(record) {
					Ext.each(record.get('tags') || [], function(tag) {
								if (array.indexOf(tag.name) == -1)
									array.push(tag.name);
							}, this);
				}, this);

		Ext.each(array, function(a) {
					storeArray.push([a]);
				}, this);

		var newStore = new Ext.data.SimpleStore({
					fields	: ['tag'],
					data	: storeArray
				});

		this.tagsStore = newStore;

		if (this.newForm)
			this.newForm.updateTagsField(this.tagsStore);
	},

	/**
	 * Sets the toggled field in each of the records to true/false depending
	 * on the current toggleAllState
	 */
	toggleAll						: function() {
		// Hide them all
		if (this.toggleAllState) {
			this.store.each(function(record) {
						record.set('toggled', true);
					}, this);

			return this.toggleAllState = false;
		};

		// Show them all
		this.store.each(function(record) {
					record.set('toggled', false);
				}, this);

		return this.toggleAllState = true;
	},

	initalizeEditableMouseover		: function() {
		// var dataview = this.sortableDataView.dataview;
		// if (!dataview) return;
		// 
		// Ext.each(dataview.getNodes(), function(node) {
		// var record = dataview.getRecord(node),
		// node = Ext.get(node),
		// els = node.select('div');
		// 	
		// if (record.get('has_permission')) {
		// els.each(function(el) {
		// var element = Ext.get(el);
		// 			
		// Ext.each(element.dom.className.split(' '), function(cls) {
		// if (cls.split('-')[0] == "action") {
		// console.log(element.dom);
		// 					
		// element.on('mouseover', function() { console.log(element.dom); }, element);
		// // element.on('mouseout', function() { this.removeClass('x-over'); });
		// 					
		// return;
		// };
		// }, this);
		// }, this);
		// };

		// var item = Ext.get(item);
		// 			
		// item.on('mouseover', item.addClass.createDelegate(item, ['x-over']));
		// item.on('mouseout', item.removeClass.createDelegate(item, ['x-over']));
		// }, this);
		// };

		// if (!this.dataview) return;
		// 
		// var tempTask = new Ext.util.DelayedTask(function() {
		// var items = this.dataview.el.select('.x-value');
		// 	
		// if (this.idCard && this.idCard.get('has_permission')) {
		// Ext.each(items.elements, function(item) {
		// var item = Ext.get(item);
		// 			
		// item.on('mouseover', item.addClass.createDelegate(item, ['x-over']));
		// item.on('mouseout', item.removeClass.createDelegate(item, ['x-over']));
		// }, this);
		// };
		// }, this);
		// tempTask.delay(1000);
	},

	/**
	 * Returns the empty text string which is currently used for the dataview
	 * @return {String} Empty text string
	 */
	getEmptyText					: function() {
		return Ext.ux.Translator.translate('app_nothing_to_display');
	},

	/**
	 * Returns the default hbar for the window
	 */
	getDefaultHBar					: function() {
		return [{
					text	: Ext.ux.Translator.translate('nnew'),
					iconCls	: 'new',
					xtype	: 'header_toolbar_button',
					handler	: this.toggleNewForm,
					scope	: this
				}, {
					text	: Ext.ux.Translator.translate('refresh'),
					iconCls	: 'refresh',
					xtype	: 'header_toolbar_button',
					handler	: this.controller.fireAction.createDelegate(
							this.controller, ['refresh', this.controller]
					)
				}, {
					text	: Ext.ux.Translator.translate('toggle_all'),
					iconCls	: 'toggle-all',
					xtype	: 'header_toolbar_button',
					handler	: this.toggleAll,
					scope	: this
				}, '->', {
					xtype		: 'searchfield',
					listeners	: {
						scope	: this,
						'keyup'	: this.searchStore
					}
				}];
	},

	/**
	 * Gets the default window side panel content.
	 * By default, it will listen for any datastores in the main window 
	 */
	getDefaultPanel					: function() {
		var config = config || {};

		// // Check if a store is defined, if not then we cannot do anything
		// // if (!this.store) { return config; };
		// 
		// /**
		// * @property authorDataView
		// * @type Ext.DataView
		// * The dataview which will display the authors of all the currently loaded store items
		// */
		// var authorDataView = new Ext.DataView({
		// emptyText: Ext.ux.Translator.translate('no_items_to_display'),
		// singleSelect: true,
		// autoHeight: true,
		// autoScroll: true,
		// overClass: 'x-view-over',
		// itemSelector: 'div.item-wrap',
		// store: this.getAuthors(),
		// tpl: new Ext.XTemplate(
		// '<tpl for=".">',
		// '<div class="item-wrap">',
		// '<div>{full_name}</div>',
		// '</div>',
		// '</tpl>'
		// )
		// });
		// 
		// config.items = [authorDataView];

		return config;
	},

	/**
	 * Looks at the current hive and get all the memberships from it, and returns it in a store
	 */
	getAuthors						: function() {
		var memberships = [];

		// We need to loop through each of these members and convert them so we have the
		// appropriate data (record.data)
		Ext.each(Kohive.OS.getCurrentHiveMemberships(), function(r) {
					// Get the records data
					var d = r.data;

					// Now each of the import field
					var array = [];
					array.push(d.id_card.nickname);
					array.push(d.id_card.email);
					array.push(String.format("{0} {1}", d.id_card.first_name,
							d.id_card.last_name
					));
					array.push(d.id_card_id);
					// array.push(d.state);

					memberships.push(array);
				}, this);

		return new Ext.data.SimpleStore({
					fields		: ['nick_name', 'email', 'full_name',
							'id_card_id'],
					data		: memberships,
					sortInfo	: {
						field		: 'full_name',
						direction	: 'ASC'
					}
				}
		);
	},

	/**
	 * Method which maximizes the window the maximum viewport size possible
	 */
	maximize						: function() {
		if (!this.maximized) {
			this.expand(false);
			this.restoreSize = this.getSize();
			this.restorePos = this.getPosition(true);
			if (this.maximizable) {
				this.tools.maximize.hide();
				this.tools.restore.show();
			};
			this.maximized = true;
			this.el.disableShadow();

			if (this.dd) {
				this.dd.lock();
			};
			if (this.collapsible) {
				this.tools.toggle.hide();
			}
			this.el.addClass('x-window-maximized');
			this.container.addClass('x-window-maximized-ct');

			this.fitContainer();
			this.doConstrain();

			this.fireEvent('maximize', this);

			this.saveState();
		};
	},

	/**
	 * Method which restores the window back to it's original size when the maximize button is
	 * pressed again.
	 */
	restore							: function() {
		if (this.maximized) {
			this.el.removeClass('x-window-maximized');
			this.tools.restore.hide();
			this.tools.maximize.show();

			// Reposition the window
			var cp = this.getContainerConstrainPositions();
			var rp = {
				x	: this.restorePos[0],
				y	: this.restorePos[1]
			};

			if (rp.x < cp.x) {
				rp.x = cp.x;
			};
			if (rp.y < cp.y) {
				rp.y = cp.y;
			};

			this.setPosition(rp.x, rp.y);

			// Resize the window
			var vs = this.getContainerViewSize();
			var rs = this.restoreSize;

			// Check if the restore width or height is more than the viewports width or height
			if (rs.width > vs.width) {
				rs.width = vs.width;
			};
			if (rs.height > vs.height) {
				rs.height = vs.height;
			};

			this.setSize(rs.width, rs.height);

			// Delete properties that aren't used anymore
			delete this.restorePos;
			delete this.restoreSize;

			this.maximized = false;
			this.el.enableShadow(true);

			if (this.dd) {
				this.dd.unlock();
			};
			if (this.collapsible) {
				this.tools.toggle.show();
			};
			this.container.removeClass('x-window-maximized-ct');

			this.doConstrain();

			this.fireEvent('restore', this);

			this.saveState();
		};
	},

	/**
	 * Method which constrains the width and the height of the window depending on the size of the
	 * viewport.
	 */
	doConstrain						: function() {
		// If the window is closed while the opening animation is running, don't run this method,
		// which makes the window appear at the end even though it was closed.
		// Especially ugly when switching hives while an app is opening
		if (this.hidden)
			return;

		// Resize and reposition the window
		var vs = this.getContainerViewSize();
		var cp = this.getContainerConstrainPositions();
		var ws = this.el.getBox();
		var bs = Ext.getBody().getBox();

		// Check for window side panel
		if (this.windowSidePanel) {
			if (!this.windowSidePanelHidden) {
				// bs.width = bs.width - this.windowSidePanel.el.getWidth();
			};
		};

		// position
		if (ws.x + ws.width > bs.width - 5) {
			ws.x = ws.x - ((ws.x + ws.width) - bs.width) - 5;
		};
		if (ws.y + ws.height > bs.height - 80) {
			ws.y = ws.y - ((ws.y + ws.height) - bs.height) - 80;
		};

		if (ws.x < cp.x) {
			ws.x = cp.x;
		};
		if (ws.y < cp.y) {
			ws.y = cp.y;
		};

		this.setPosition(ws.x, ws.y);

		// size
		if (ws.height == this.oldHeight + 1) {
			ws.height = this.oldHeight;
		};

		// Check if the restore width or height is more than the viewports width or height
		if (ws.width > vs.width) {
			ws.width = vs.width;
		};
		if (ws.height > vs.height) {
			ws.height = vs.height;
		};

		this.setSize(ws.width, ws.height);

		this.saveState();

		this.oldHeight = ws.height;
	},

	/**
	 * This is called internally by maximise.  We override it here to stop the window
	 * taking up the whole screen when maximising
	 */
	fitContainer					: function() {
		var vs = this.getContainerViewSize();

		this.setSize(vs.width, vs.height - 120);
		this.setPosition(this.getContainerConstrainPositions());
	},

	/**
	 * Returns the contrain properties for the x+y axis
	 */
	getContainerConstrainPositions	: function() {
		var ccp = {
			x	: 50,
			y	: 38
		};

		// Check for window side panel
		if (this.windowSidePanel) {
			if (!this.windowSidePanelHidden) {
				var wspw = this.windowSidePanel.el.getWidth() + 10;
				ccp = {
					x	: 50 + wspw,
					y	: 38
				};
			} else {
				ccp = {
					x	: 60,
					y	: 38
				};
			};
		};

		return ccp;
	},

	/**
	 * Gets the contains view size
	 */
	getContainerViewSize			: function() {
		var vs = this.container.getViewSize();

		vs.width = vs.width - 55;
		vs.height = vs.height;

		// console.log(vs.height);

		// Check for window side panel
		if (this.windowSidePanel) {
			if (!this.windowSidePanelHidden) {
				var wspw = this.windowSidePanel.el.getWidth() + 10;
				vs.width = vs.width - wspw;
			} else {
				vs.width = vs.width - 10;
			};
		};

		return vs;
	},

	/**
	 * Called whenever the window is activated, this adds an entry to the Ext.History singleton
	 * so that the back button takes the user back to the previous window.  Only adds this window's
	 * url if it is not already the current url. Also adds the current hive url slug to the history
	 * token.
	 */
	addHistoryToken					: function() {
		var url = this.initialConfig.url || this.url;
		if (url && url != Ext.History.getToken()) {
			var hiveId = Kohive.OS.getCurrentHive().data.hive.id;

			if (hiveId) {
				Ext.History.add(hiveId + '/' + url);
			};
		};
	},

	/**
	 * Called whenever the window is deactived/closed, this removes the bookmarks url from the history
	 * token and returns the value back to just the url slug.
	 */
	removeHistoryToken				: function() {
		// Get the current hive ID
		var hiveId = Kohive.OS.getCurrentHive().data.hive.id;

		// Loop through each of the open windows and check if there is more than 1 open
		var i = 0;
		Kohive.OS.getWindowManager().each(function(w) {
					if (!w.hidden)
						i++;
				}, this);

		// If the hive id has been set and there is only 1 window open, update the history token
		if (hiveId && i < 2) {
			return Ext.History.add(hiveId);
		};
	},

	// private
	initEvents						: function() {
		Kohive.Window.superclass.initEvents.call(this);

		this.addEvents(
				/**
		* @event beforeclosebyuser
		* Fires before the window is closed by the user (via click on x button)
		*/
				'beforeclosebyuser',

				/**
				* @event beforeclosebyuser
				* Fires after the window is closed by the user(via click on x button)
				*/
				'closebyuser'
		);

		if (this.animateTarget) {
			this.setAnimateTarget(this.animateTarget);
		}

		if (this.resizable) {
			this.resizer = new Ext.Resizable(this.el, {
						minWidth		: this.minWidth,
						minHeight		: this.minHeight,
						handles			: this.resizeHandles || "all",
						pinned			: true,
						resizeElement	: this.resizerAction
					});
			this.resizer.window = this;
			this.resizer.on("beforeresize", this.beforeResize, this);
		}

		if (this.draggable) {
			this.dragEl = this.el.child('.x-window-tc');
			this.dragEl.dom.id = this.el.id + '-drag';
			this.dragEl.addClass('x-window-draggable');
		}
		this.initTools();

		this.el.on("mousedown", this.toFront, this);
		this.manager = this.manager || Ext.WindowMgr;
		this.manager.register(this);
		this.hidden = true;
		if (this.maximized) {
			this.maximized = false;
			this.maximize();
		}
		if (this.closable) {
			var km = this.getKeyMap();
			km.on(27, this.onEsc, this);
			km.disable();
		};
	},

	/**
	* Sets up listeners to events emitted by the application controller
	*/
	initListeners					: function() {
		this.on('render', this.setupEditable, this);
	},

	/**
	 * Overwrite the addTool method so we can style it differently
	 */
	addTool							: function() {
		if (!this[this.toolTarget]) { // no where to render tools!
			return;
		};
		if (!this.toolTemplate) {
			// initialize the global tool template on first use
			var tt = new Ext.Template('<div class="x-tool x-tool-{id}">&#160;</div>'
			);
			tt.disableFormats = true;
			tt.compile();
			Ext.Panel.prototype.toolTemplate = tt;
		};
		if (!this.toolInnerTarget) {
			// Check if the inner tools element has been created yet
			this.toolInnerTarget = this.el.child('.x-window-tl').createChild({
						cls	: 'x-tools'
					});
		};
		for (var i = 0, a = arguments, len = a.length; i < len; i++) {
			var tc = a[i];
			if (!this.tools[tc.id]) {
				var target;
				if (tc.id == 'collapse-header') {
					target = this.el.child('.x-window-tl');
				} else {
					target = this.toolInnerTarget;
				};
				var overCls = 'x-tool-' + tc.id + '-over';
				var t = this.toolTemplate.insertFirst((tc.align !== 'left')
								? target
								: target.child('span'), tc, true);
				this.tools[tc.id] = t;
				t.enableDisplayMode('block');
				t.unselectable();
				t.on('click', this.createToolHandler(t, tc, overCls, this));
				if (tc.on) {
					t.on(tc.on);
				};
				if (tc.hidden) {
					t.hide();
				};
				if (tc.qtip) {
					if (typeof tc.qtip == 'object') {
						Ext.QuickTips.register(Ext.apply({
									target	: t.id
								}, tc.qtip));
					} else {
						t.dom.qtip = tc.qtip;
					};
				};
				t.addClassOnOver(overCls);
			};
		};
	},

	/**
	 * Slides the New Bookmark form into view depending on its display status
	 * @param {Boolean} dontFocus True if you dont want to focus the first field
	 */
	toggleNewForm					: function(dontFocus) {
		var f = this.newForm || null;

		if (f) {
			f.toggleCollapse();
			f.doLayout();

			if (dontFocus === true)
				return;

			// Get the first item and focus on it
			var field = f.find()[0];
			if (field) {
				var tempTask = new Ext.util.DelayedTask(function() {
							field.focus();
						}, this);
				tempTask.delay(500);
			};
		};
	},

	/**
	 * Adds event listeners for the status bar so it can update when the events happen
	 */
	addStatusBarEvents				: function() {
		// If there is no statusbar, return
		if (!this.statusBar) {
			return;
		};

		// If there is no store, return
		if (!this.store) {
			return this.hideLoading();
		};

		// Before the store loads, show the loading bookmarks text
		this.store.on('beforeload', function() {
					this.showLoading();
				}, this);

		// Once the store has loaded hide it
		this.store.on('load', function() {
			this.hideLoading();

			Ext.each(this.store.data.items, function(record) {
				var data = record.data;

				// Get the id card
				Ext.applyIf(data, {
					id_card	: Kohive.OS
							.getIdCardFromMembershipId(data.author_membership_id)
				}
				);
			}
			);
		}, this
		);

		// if the store fails to load, show the error
		this.store.on('loadexception', function() {
					this.showFailure();
				}, this);
	},

	/**
	 * Returns the default status bar for the window
	 */
	getDefaultStatusBar				: function() {
		this.statusBar = new Ext.StatusBar();
		this.showLoading();

		// add drag image
		this.on('render', function() {
					var ct = this.el.child('.x-window-bwrap');
					if (!ct)
						return;

					this.dragger = ct.createChild({
								cls	: 'x-drag-icon'
							});
				}, this);

		return this.statusBar;
	},

	/**
	 * Convenience method for setting the status bar to loading
	 * @param {String} message Custom loading message
	 */
	showLoading						: function(message) {
		var message = (message && typeof message == "string")
				? message
				: Ext.ux.Translator.translate('app_statusbar_submitting');
		this.statusBar.setStatus({
					text	: message,
					iconCls	: 'loading-icon'
				});
	},

	showMessage						: function(message) {
		if (!message)
			return;

		this.statusBar.addClass('x-hover');

		this.statusBar.setStatus({
					text	: message,
					iconCls	: ''
				});
	},

	/**
	 * Convenience method for removing the loading status from the status bar
	 */
	hideLoading						: function() {
		this.statusBar.removeClass('x-hover');

		// TODO: for some reason this is not working when changing hives. i'm guessing it is something to do
		// with the hive windows closing, then it being called late or something...
		if (!this.statusBar || !this.statusBar.clearStatus)
			return;

		this.statusBar.clearStatus();
	},

	/**
	 * Convenience method for setting the status bar to refreshing
	 * @param {String} message custom message for the refreshing message
	 */
	showRefreshing					: function(message) {
		var message = (message && typeof message == "string")
				? message
				: Ext.ux.Translator.translate('app_statusbar_submitting');
		this.showLoading(Ext.ux.Translator
				.translate('app_statusbar_refreshing'));
	},

	/**
	 * Convenience method for setting the status bar to submitting
	 * @param {String} message custom message for the refreshing message
	 */
	showSubmitting					: function(message) {
		var message = (message && typeof message == "string")
				? message
				: Ext.ux.Translator.translate('app_statusbar_submitting');
		this.showLoading(message);
	},

	/**
	 * Convenience method for setting the status bar to updating
	 * @param {String} message custom message for the refreshing message
	 */
	showUpdating					: function(message) {
		var message = (message && typeof message == "string")
				? message
				: Ext.ux.Translator.translate('app_statusbar_updating');
		this.showLoading(message);
	},

	/**
	 * Convenience method for setting the status bar to creating
	 * @param {String} message custom message for the refreshing message
	 */
	showCreating					: function(message) {
		var message = (message && typeof message == "string")
				? message
				: Ext.ux.Translator.translate('app_statusbar_creating');
		this.showLoading(message);
	},

	/**
	 * Convenience method for setting the status bar to destroying
	 * @param {String} message custom message for the refreshing message
	 */
	showDestroying					: function(message) {
		var message = (message && typeof message == "string")
				? message
				: Ext.ux.Translator.translate('app_statusbar_destroying');
		this.showLoading(message);
	},

	/**
	 * Convenience method for setting the status bar to success
	 * @param {String} message Custom loading message
	 */
	showSuccess						: function(message) {
		var message = (message && typeof message == "string")
				? message
				: Ext.ux.Translator.translate('app_statusbar_success');
		this.statusBar.setStatus({
					text	: message,
					iconCls	: 'success-icon'
				});

		var tempTask = new Ext.util.DelayedTask(function() {
					this.hideLoading();
				}, this);
		tempTask.delay(5000);
	},

	/**
	 * Convenience method for setting the status bar to failure
	 * @param {String} message Custom loading message
	 */
	showFailure						: function(message) {
		var message = (message && typeof message == "string")
				? message
				: Ext.ux.Translator.translate('app_statusbar_failure');
		this.statusBar.setStatus({
					text	: message,
					iconCls	: 'failure-icon'
				});

		var tempTask = new Ext.util.DelayedTask(function() {
					this.hideLoading();
				}, this);
		tempTask.delay(5000);
	},

	createGhost						: function(cls, useShim, appendTo) {
		var el = document.createElement('div');
		el.id = this.el.id + '-ghost';
		el.className = 'x-panel-ghost ' + (cls ? cls : '');

		if (this.header) {
			el.appendChild(this.el.dom.firstChild.cloneNode(true));
		};

		// Calculate the height of the window
		var height = 0;
		// if (this.header || this.hbar) { height = height + this.el.child('.x-window-tl').getHeight(); };
		height = height + this.bwrap.getHeight();

		if (this.hbar) {
			height = height + this.el.child('.x-window-toolbar-r').getHeight();
			Ext.fly(el).child('.x-window-toolbar-r').remove();
		};

		// Set the height and width of the ghost
		Ext.fly(el).setHeight(height);
		var ghost = Ext.fly(el.appendChild(document.createElement('ul')))
				.setHeight(height);
		el.style.width = this.el.dom.offsetWidth + 'px';

		this.container.dom.appendChild(el);

		// Check for window side panel
		if (this.windowSidePanel) {
			if (!this.windowSidePanelHidden) {
				var ghostSide = Ext.fly(el.appendChild(document
						.createElement('ul')))
						.setHeight(this.windowSidePanel.el.getHeight())
						.setWidth(this.windowSidePanel.el.getWidth())
						.addClass('sidePanel');
				var thisEl = Ext.get(el.id);
				// console.log(thisEl);
				ghostSide.anchorTo(thisEl, 'r-l', [0, 25]);
			};
		};

		if (useShim !== false && this.el.useShim !== false) {
			var layer = new Ext.Layer({
						shadow		: false,
						useDisplay	: true,
						constrain	: false
					}, el);
			layer.show();
			return layer;
		} else {
			return new Ext.Element(el);
		};
	},

	/**
	 * Wrapper around hide method, to fire a special event when window is closed (hidden) by user to differentiate
	 * between hidden by user and hidden by system (when switching hives)
	 */
	closeByUser						: function() {
		if (this.fireEvent("beforeclosebyuser", this) !== false) {
			this.hide();
			this.fireEvent('closebyuser', this);
		}
	},

	/**
	 * Searching the store
	 */
	searchStore						: function(field, value) {
		var value = (value === true) ? field : field.getValue();

		// check if store exists
		if (!this.store)
			return;

		// check if the value has spaces, if it does change it into an array
		// TODO add implementation for ||
		if (value.split(' ').length > 1)
			value = value.split(' ');

		// clear the current filters
		this.store.clearFilter();

		// filter the store
		this.store.filterBy(function(record, id) {
			var ignoreFields = ['id', 'hive_id', 'can_edit', 'can_delete',
					'created_at', 'updated_at', 'modified_at', 'tags'], result = false;

			if (typeof value == "object") {
				var str = "";
				Ext.each(value, function(v, i) {
							// add the value to the str
							str += v;

							// add the | if it isnt the last one
							if (i != value.length - 1) {
								if (value[i + 1] != "")
									str += "|";
							};
						}, this);

				regExp = new RegExp(str, 'i');
			} else {
				regExp = new RegExp(value, 'i');
			};

			record.fields.each(function(field) {
						if (result === true)
							return;
						if (ignoreFields.indexOf(field.name) == -1)
							if (regExp.test(record.get(field.name)))
								result = true;
					}, this);

			return result;
		}, this
		);
	},

	/**
	* 
	 * @param {Ext.DataView} dataview The dataview which has all the records
	 * @param {Integer} index The index of the node in question
	 * @param {HTMLElement} node The node in question
	 * @param {Ext.EventObject} event The event fired
	 */
	viewOnMouseOver					: function(dataview, index, node, el) {
		var el = Ext.get(node), record = dataview.getRecord(node);

	},

	/**
	 * 
	 * @param {Ext.DataView} dataview The dataview which has all the records
	 * @param {Integer} index The index of the node in question
	 * @param {HTMLElement} node The node in question
	 * @param {Ext.EventObject} event The event fired
	 */
	viewOnMouseOut					: function(dataview, index, node, el) {
		var el = Ext.get(node), record = dataview.getRecord(node);

	},

	/**
	 * Called when the windows dataview has been clicked. it has to do a few things:
	 *  - toggle the pod
	 *  - delete stuff
	 * 
	 * @param {Ext.DataView} dataview The dataview which has all the records
	* @param {Integer} index The index of the node in question
	* @param {HTMLElement} node The node in question
	* @param {Ext.EventObject} event The event fired
	 */
	viewOnClick						: function(dataview, index, node, e) {
		var el = Ext.get(node), target = Ext.get(e.getTarget()), record = dataview
				.getRecord(node);

		// Toggle
		if (target.hasClass('x-control')
				|| target.parent().hasClass('x-control')) {
			(record.get('toggled') === true)
					? record.set('toggled', false)
					: record.set('toggled', true);
		};

		// Delete
		if (target.hasClass('x-delete') || target.parent().hasClass('x-delete')) {
			var title = el.child('.x-title div');
			if (title)
				title = title.dom.innerHTML;

			var contentEl = el.child('.x-content-wrap') || el;

			return Ext.Msg.show({
						title	: '',
						msg		: Ext.ux.Translator.translate('app_'
										+ this.model.modelName.toLowerCase()
												.singularize()
										+ '_destroy_desc', {
									title	: title || null
								}),
						buttons	: {
							yes	: Ext.ux.Translator.translate('ddelete'),
							no	: Ext.ux.Translator.translate('keep')
						},
						scope	: this,
						fn		: function(btn) {
							if (btn == "yes")
								this.controller.fireAction('destroy',
										this.controller, [record, this.store,
												contentEl]
								);
						}
					});
		};
	},

	/**
	 *  Method called when the dataview store has been loaded.
	 * 
	 * @param {Ext.data.Store} store The store which has been loaded
	 */
	viewOnLoad						: function(store) {
		var dataview = this.sortableDataView.dataview;
		if (!dataview)
			return;

		var nodes = dataview.getNodes();

		Ext.each(nodes, function(node) {
			var record = dataview.getRecord(node), el = Ext.get(node);

			if (record.get('can_edit')) {
				// editable rollovers
				var items = el.select('div'), fields = [];

				Ext.each(items.elements || [], function(item) {
							var item = Ext.get(item);

							Ext.each(item.dom.className.split(' '), function(
											cls) {
										if (cls.split('-')[0] == "action")
											fields.push(item);
									}, this);
						}, this);

				Ext.each(fields, function(field) {
							field.on('mouseover', field.addClass
											.createDelegate(field, ['x-over']));
							field.on('mouseout', field.removeClass
											.createDelegate(field, ['x-over']));
						}, this
				);
			};
		}, this
		);
	},

	/**
	 * Stores an array of all toggled records in the current store.
	 * The index will contain the id of the record, and the value will contain the 'toggled' param
	 */
	currentToggledStates			: [],

	/**
	 * Method called on 'beforeload' of the store in the Ext.Window
	 * Loops through each of the records and saves the 'toggled' state into the currentToggledStates
	 * property of the window.
	 * @param {Ext.data.Store} store The current store (before load)
	 */
	setToggledStates				: function(store) {
		if (!store)
			return;

		this.currentToggledStates = [];
		store.each(function(record) {
			this.currentToggledStates[record.get('id')] = record.get('toggled');
		}, this
		);
	},

	/**
	 * Method called when the store has been loaded ('load')
	 * Loops through each of the records in the store and checks if any records have a toggled state
	 * saved in the windows currentToggledStates property and set's it if nessecary
	 * @param {Ext.data.Store} store The new store (after load)
	 */
	getToggledStates				: function(store) {
		if (!store || this.currentToggledStates.length == 0)
			return;

		store.each(function(record) {
					var id = record.get('id');
					if (this.currentToggledStates[id] === true
							|| this.currentToggledStates[id] === false)
						record.set('toggled', this.currentToggledStates[id]);
				}, this);
	}
}
);

/**
 * Overwrite the default drag implementation
 */
Ext.Window.DD = function(win) {
	this.win = win;
	Ext.Window.DD.superclass.constructor.call(this, win.el.id, 'WindowDD-'
					+ win.id);

	this.setHandleElId(win.id + '-drag');
	this.scroll = false;
};
Ext.extend(Ext.Window.DD, Ext.dd.DD, {
			moveOnly	: true,
			startDrag	: function() {
				var w = this.win;

				this.proxy = w.ghost();

				var so = w.el.shadowOffset;

				// Check for window side panel
				if (w.windowSidePanel) {
					if (!w.windowSidePanelHidden) {
						var wspw = w.windowSidePanel.el.getWidth() + 10;
						this.constrainTo(w.container, {
									top		: 38,
									right	: 5,
									left	: 50 + wspw,
									bottom	: 80
								});
					} else {
						this.constrainTo(w.container, {
									top		: 38,
									right	: 5,
									left	: 60,
									bottom	: 80
								});
					};
				} else {
					this.constrainTo(w.container, {
								top		: 38,
								right	: 5,
								left	: 50,
								bottom	: 80
							});
				};
			},

			b4Drag		: Ext.emptyFn,

			onDrag		: function(e) {
				this.alignElWithMouse(this.proxy, e.getPageX(), e.getPageY());
			},

			endDrag		: function(e) {
				// Remove the ghost
				this.win.unghost();

				// Update the state
				this.win.saveState();

				// doLayout
				// this.win.doLayout();
			}
		});

Ext.reg('kohive_window', Kohive.Window);